/***************************************************
 * mode name:BackgroundProgram
 * brief:用于管理所有任务，与mainWindow做区分
 * @author yxt
 * @created 2024/09/04 21:20:29
 * @imfor ~/bigSystem/closedLoop_regulation/src/BackgroundProgram.cpp
 * @modify:
 * 		
***************************************************/
#include "BackgroundProgram.h"
#include "ConfigImport.h"
#include "EEG_device.h"
#include "SimEEG_device.h"
#include "sys_data_struct.h"
#include "EmotionFilter.h"
#include <QThreadPool>
#include <QRunnable>

#include <armadillo>

#define DEBUG
#define GET_TIME

// 每1000个点保存一次
#define POINTNUM_OF_SAVE 1000  
// 启用线程池
#define THREAD_POOL

BackgroundProgram::BackgroundProgram(ConfigImport *sys_config, QObject *parent):
    QObject(parent),
    sys_config(sys_config),
    _eegData_single(sys_config->channalNum),
    _eegData_oneTrial(sys_config->channalNum),
    org_eegData(sys_config->channalNum),
    filt_eegData(sys_config->channalNum)
{
    eeg_dev = nullptr;
    simServer = nullptr;
    // org_eegData初始化为空，所以索引为-1
    pointIndex = -1;
    org_eegData.data.reserve(1900000);// 1秒1000个点，预留30分钟的数据空间
    // org_eegData.data.reserve(sys_config->pointPerTrial);

    // 脑网络生成层
#ifdef THREAD_POOL
    // 配置线程池
    QThreadPool::globalInstance()->setMaxThreadCount(4);  // 设置线程池的最大线程数
    networkCore = new NetworkCore();
    connect(networkCore, &NetworkCore::send_network, this, &BackgroundProgram::slt_recNetwork);
        
#else
    networkCore = new NetworkCore();
#endif // THREAD_POOL

    
}

BackgroundProgram::~BackgroundProgram(){
    if(networkCore){
        delete networkCore;
        networkCore = nullptr;
    }
}

int BackgroundProgram::collectingDeviceInit(){
    // 实例化仿真的采集类
    if(ConfigImport::getInstance()->use_simulationDev()){
        #ifdef DEBUG
        qDebug() << "sim";
        #endif // DEBUG

        // 仿真设备
        eeg_dev = new SimEEG_device();
        if(eeg_dev == nullptr){
            printf("SimEEG_device error\n");
            return -1;
        }
        connect(dynamic_cast<SimEEG_device*>(eeg_dev),
            // static_cast<void (EEG_device::*)(const QVariant&)>(&EEG_device::send_ClientData),
            &SimEEG_device::send_ClientData,
            this, 
            // static_cast<void (BackgroundProgram::*)(const QVariant&)>(&BackgroundProgram::slt_recEEGData)
            &BackgroundProgram::slt_recEEGData
        );
    }
    // 实例化无线采集设备类
    else{
        eeg_dev = EEG_device::getInstance();
        // eeg_dev线程开始运行时，将emit send_ClientData(const std::vector <EEG_VOLTAGE>& eegData);
        connect(dynamic_cast<EEG_device*>(eeg_dev),
            // static_cast<void (EEG_device::*)(const QVariant&)>(&EEG_device::send_ClientData),
            &EEG_device::send_ClientData,
            this, 
            // static_cast<void (BackgroundProgram::*)(const QVariant&)>(&BackgroundProgram::slt_recEEGData)
            &BackgroundProgram::slt_recEEGData
        );
    }

    eeg_dev->collectingEEG(false);
    return 0;
}

void BackgroundProgram::collectingData(){
    if(ConfigImport::getInstance()->use_simulationDev()){
        // 使用子进程调用仿真服务器
        simServer = new QProcess();
        // 重定向子进程的输出位置，调试用
        // simServer->setStandardOutputFile("../bin/temp_log.txt");
        // simServer->setStandardErrorFile("../bin/temp_log.txt");  // 将标准错误流也重定向到 temp_log 文件
        simServer->start("../bin/sim_eegDev");
    }
    
    eeg_dev->collectingEEG(true);
    eeg_dev->start();
}

/**********************************
 * 函数名：stopCollecting
 * 功能：
 * 输入：无
 * 输出：无
 * 备注：无
**********************************/
void BackgroundProgram::stopCollecting(){
    eeg_dev->stopCollecting();

    if(simServer != nullptr){
        simServer->terminate(); // 请求优雅停止
        if (!simServer->waitForFinished(3000)) {  // 等待3秒，如果未退出，则强制杀死
            simServer->kill();  // 强制停止
            std::cout << "kill simServer" << std::endl;
        }
        std::cout << "simServer is closed" << std::endl;
        delete simServer;
        simServer = nullptr;
    }
}

void BackgroundProgram::slt_recEEGData(const QVariant& data){
    // auto BT_slt = std::chrono::steady_clock::now();
    std::unique_lock<std::mutex> lock(_eegDataMutex); // 加锁
    static int counts=0;
    // 无线设备数据类型
    if(data.canConvert<std::vector<EEG_VOLTAGE>>()){
        std::vector<EEG_VOLTAGE> eegData_rec = data.value<std::vector<EEG_VOLTAGE>>();

        // 获取通道数(64)
        // uint8_t channalNum = sizeof(eegData_rec[0].data) / sizeof(eegData_rec[0].data[0]);
        // _eegData_single.channalNum = sizeof(eegData_rec[0].data) / sizeof(eegData_rec[0].data[0]);
        _eegData_single.data.clear();
        for(const auto& voltage : eegData_rec){
            std::array<double, TCPEEG_CHANNEL_COUNT> temp_arr;
            for(size_t i = 0; i<TCPEEG_CHANNEL_COUNT; ++i){
                temp_arr[i] = voltage.data[i];
            }
            _eegData_single.data.push_back(temp_arr);
        }
    }
    // 仿真数据类型
    else if(data.canConvert<arma::mat>()){
        arma::mat eegData_rec = data.value<arma::mat>();
        _eegData_single.data.clear();

        _eegData_single.dataHead.channalNum = eegData_rec.n_rows;
        EEGDataArr temp_arr;
        for(int index=0; index<eegData_rec.n_cols; ++index){
            std::memcpy(temp_arr.data(), eegData_rec.colptr(index), eegData_rec.n_rows * sizeof(double)); // 拷贝列数据
            _eegData_single.data.push_back(temp_arr);
        }
    }

    for(auto &it:_eegData_single.data){
        org_eegData.data.push_back(it);
        ++pointIndex;
        _eegData_oneTrial.data.push_back(it);
        // 累积到一个trail的点数,传递出去
        if(_eegData_oneTrial.data.size() == sys_config->pointPerTrial){
            // emit send_oneTrialData(_eegData_oneTrial);
            networkCore->network_created(_eegData_oneTrial);
            _eegData_oneTrial.data.clear();
        }
    }

    
    // 做一次转发
    emit send_singleData(_eegData_single);
    // auto AT_slt = std::chrono::steady_clock::now();
    // double duration_millsecond = std::chrono::duration<double, std::milli>(AT_slt - BT_slt).count();
    // std::cout << "slt_times:" << duration_millsecond << "ms" << std::endl;
}

void BackgroundProgram::slt_recNetwork(const arma::rowvec &network){
    emit send_network(network);
}

/**********************************
 * 函数名：slt_setDataLabel
 * 功能：设置标签
 * 输入：标签类型
 * 输出：无
 * 备注：无
**********************************/
void BackgroundProgram::slt_setDataLabel(EmotionInterface::EmoMode label, int playMode, bool is_begin){
    if(playMode == -1){
        return;
    }

    if(playMode == 0){
        slt_setDataLabel_train(label, is_begin);
    }
    else if(playMode == 1){
        slt_setDataLabel_test(label, is_begin);
    }
}
void BackgroundProgram::slt_setDataLabel_train(EmotionInterface::EmoMode label, bool is_begin){
    // 用于记录当前是在第几个视频，每次视频结束时自加
    static uint8_t negVideoIndex = 0;
    static uint8_t neuVideoIndex = 0;
    static uint8_t posVideoIndex = 0;

    switch (label)
    {
        case EmotionInterface::EmoMode::Negative:
        // 消极标签
        if(is_begin){
            // 视频开始标签
            org_eegData.dataHead.neg_lable_beginIndex[negVideoIndex] = pointIndex;
            std::cout << "negVideoIndex-begin pointIndex:" << pointIndex << std::endl;
        }
        else{
            // 视频结束标签
            org_eegData.dataHead.neg_lable_endIndex[negVideoIndex] = pointIndex;
            std::cout << "negVideoIndex-end pointIndex:" << pointIndex << std::endl;
            ++negVideoIndex;
        }
        break;

        case EmotionInterface::EmoMode::Neutral:
        // 中性标签
        if(is_begin){
            // 视频开始标签
            org_eegData.dataHead.neu_lable_beginIndex[neuVideoIndex] = pointIndex;
            std::cout << "neuVideoIndex-begin pointIndex:" << pointIndex << std::endl;

        }
        else{
            // 视频结束标签
            org_eegData.dataHead.neu_lable_endIndex[neuVideoIndex] = pointIndex;
            std::cout << "neuVideoIndex-end pointIndex:" << pointIndex << std::endl;
            ++neuVideoIndex;
        }
        break;

        case EmotionInterface::EmoMode::Positive:
        // 积极标签
        if(is_begin){
            // 视频开始标签
            org_eegData.dataHead.pos_lable_beginIndex[posVideoIndex] = pointIndex;
            std::cout << "posVideoIndex-begin pointIndex:" << pointIndex << std::endl;
        }
        else{
            // 视频结束标签
            org_eegData.dataHead.pos_lable_endIndex[posVideoIndex] = pointIndex;
            std::cout << "posVideoIndex-end pointIndex:" << pointIndex << std::endl;
            ++posVideoIndex;
        }
        break;

        default:
            break;
    }
}

void BackgroundProgram::slt_setDataLabel_test(EmotionInterface::EmoMode label, bool is_begin){
    switch (label)
    {
        case EmotionInterface::EmoMode::Negative:
        // 消极标签
        if(is_begin){
            // 视频开始标签
            org_eegData.dataHead.neg_lable_beginIndex_test[0] = pointIndex;
            std::cout << "negVideoIndex-begin_test pointIndex:" << pointIndex << std::endl;
        }
        else{
            // 视频结束标签
            org_eegData.dataHead.neg_lable_endIndex_test[0] = pointIndex;
            std::cout << "negVideoIndex-end_test pointIndex:" << pointIndex << std::endl;
        }
        break;

        case EmotionInterface::EmoMode::Neutral:
        // 中性标签
        if(is_begin){
            // 视频开始标签
            org_eegData.dataHead.neu_lable_beginIndex_test[0] = pointIndex;
            std::cout << "neuVideoIndex-begin_test pointIndex:" << pointIndex << std::endl;

        }
        else{
            // 视频结束标签
            org_eegData.dataHead.neu_lable_endIndex_test[0] = pointIndex;
            std::cout << "neuVideoIndex-end_test pointIndex:" << pointIndex << std::endl;
        }
        break;

        case EmotionInterface::EmoMode::Positive:
        // 积极标签
        if(is_begin){
            // 视频开始标签
            org_eegData.dataHead.pos_lable_beginIndex_test[0] = pointIndex;
            std::cout << "posVideoIndex-begin_test pointIndex:" << pointIndex << std::endl;
        }
        else{
            // 视频结束标签
            org_eegData.dataHead.pos_lable_endIndex_test[0] = pointIndex;
            std::cout << "posVideoIndex-end_test pointIndex:" << pointIndex << std::endl;
        }
        break;

        default:
            break;
    }
}

/**********************************
 * 函数名：get_eegData
 * 功能：拷贝_eegData_single的数据出来，线程安全
 * 输入：target:拷贝出去的mat变量
 * 输出：单通道的点数
 * 备注：无
**********************************/
int BackgroundProgram::get_eegData(GenEEGData& target){
    std::unique_lock<std::mutex> lock(_eegDataMutex); // 加锁
    target = _eegData_single;
    _eegData_single.data.clear();
    return target.data.size();
}

/**********************************
 * 函数名：get_network
 * 功能：拷贝_eegData_single的数据出来，线程安全
 * 输入：target:拷贝出去的mat变量
 * 输出：单通道的点数
 * 备注：无
**********************************/
arma::rowvec BackgroundProgram::get_network() const{
    if(!networkCore){
        std::cerr << "networkCore is empty" << std::endl;
        return arma::rowvec();
    }
    return networkCore->get_network();
}


GenEEGData BackgroundProgram::get_eegData(){
    std::unique_lock<std::mutex> lock(_eegDataMutex); // 加锁
    GenEEGData res = _eegData_single;
    _eegData_single.data.clear();
    return res;
}

long long BackgroundProgram::get_pointIndex(){ 
    std::unique_lock<std::mutex> lock(_eegDataMutex); // 加锁
    return pointIndex;
}


const QMap<int, QString>& BackgroundProgram::getElectrodeNameMap(){
    return eeg_dev->getElectrodeMap();
}

/**********************************
 * 函数名：CropValidData
 * 功能：裁剪出有效的数据
 * 输入：label:标签向量
 * 输出：将数据按照trail大小切割好，输出cube结构数据
 * 备注：无
**********************************/
arma::cube BackgroundProgram::cropValidData(std::vector<int> &label){
    size_t writeIndex = 0;
    size_t readIndex = org_eegData.dataHead.neg_lable_beginIndex[0];
    std::vector<std::pair<size_t, size_t>> rangesToRemove;

    #if 1
    // 整形后的数据
    arma::cube outData;

    // 第9个字节开始是视频标签点数下标数据
    long* first_indexPtr = (long*)(&org_eegData.dataHead.neg_lable_beginIndex);
    long* second_indexPtr = first_indexPtr + TRAIN_VIDEO_NUM;

    // 三类视频(负、中、正)
    for(int kinds =0; kinds < 3; ++kinds){
        int trialPerKinds = 0;      // 记录一个类的视频的总trial数
        // 每类视频TRAIN_VIDEO_NUM个
        for(int times = 0; times < TRAIN_VIDEO_NUM; ++times){
            // std::cout << "first_indexPtr:" << *first_indexPtr <<std::endl;
            // std::cout << "second_indexPtr:" << *second_indexPtr <<std::endl;
            int slices = (*second_indexPtr - *first_indexPtr + 1) / sys_config->pointPerTrial;
            std::cout << "slices:" << slices << std::endl;

            long endIndex = *first_indexPtr + sys_config->pointPerTrial * slices - 1;

            size_t colsPerSegment = sys_config->pointPerTrial;
            // 截取出一段数据
            // 判断索引是否溢出
            if(*first_indexPtr+1 > org_eegData.data.size() || endIndex+1 > org_eegData.data.size()){
                std::cerr << "In BackgroundProgram::cropValidData, data index error!!" << std::endl;
                std::cerr <<  "org_eegData.data.size():" << org_eegData.data.size() << " first_indexPtr:" << *first_indexPtr << " endIndex:" << endIndex << std::endl;
                return arma::cube();
            }
            std::vector<EEGDataArr> temp(org_eegData.data.begin() + *first_indexPtr, org_eegData.data.begin() + endIndex);

            arma::cube result(sys_config->channalNum, colsPerSegment, slices, arma::fill::none);
            // 填充数据
            for (size_t slicesIndex = 0; slicesIndex < slices; ++slicesIndex) {
                double* target_ptr = result.slice(slicesIndex).memptr(); // 获取目标 slice 的内存首地址
                std::memcpy(target_ptr, temp.data() + slicesIndex * 1000, sizeof(EEGDataArr) * 1000);
            }

            if(outData.empty()){
                outData = result;
            }
            else{
                outData.insert_slices(outData.n_slices, result);
            }

            std::cout << "outData.n_slices:" << outData.n_slices << std::endl;
            std::cout << "result.n_slices:" << result.n_slices << std::endl;


            first_indexPtr++;
            second_indexPtr = first_indexPtr + TRAIN_VIDEO_NUM;
            trialPerKinds += slices;
        }
        // 按照正、中、负的顺序插入标签
        label.insert(label.end(), trialPerKinds, kinds-1);

        
        first_indexPtr += TRAIN_VIDEO_NUM;
        second_indexPtr = first_indexPtr + TRAIN_VIDEO_NUM;
    }

    return outData;



    #else
    // 9个视频，移除9段休息时间的无效数据
    // 改变视频个数时这里要跟着修改
    // rangesToRemove.push_back(std::pair<size_t, size_t>(0, org_eegData.dataHead.neg_lable_beginIndex[0] - 1));   // 移除第一个视频开始之前的无效数据
    rangesToRemove.push_back(std::pair<size_t, size_t>(org_eegData.dataHead.neg_lable_endIndex[0] + 1, 
                                                        org_eegData.dataHead.neg_lable_beginIndex[1]));   // 移除一二间无效数据，下同

    rangesToRemove.push_back(std::pair<size_t, size_t>(org_eegData.dataHead.neg_lable_endIndex[1] + 1, 
                                                        org_eegData.dataHead.neg_lable_beginIndex[2]));

    rangesToRemove.push_back(std::pair<size_t, size_t>(org_eegData.dataHead.neg_lable_endIndex[2] + 1, 
                                                        org_eegData.dataHead.neu_lable_beginIndex[0]));

    rangesToRemove.push_back(std::pair<size_t, size_t>(org_eegData.dataHead.neu_lable_endIndex[0] + 1, 
                                                        org_eegData.dataHead.neu_lable_beginIndex[1]));

    rangesToRemove.push_back(std::pair<size_t, size_t>(org_eegData.dataHead.neu_lable_endIndex[1] + 1, 
                                                        org_eegData.dataHead.neu_lable_beginIndex[2]));

    rangesToRemove.push_back(std::pair<size_t, size_t>(org_eegData.dataHead.neu_lable_endIndex[2] + 1, 
                                                        org_eegData.dataHead.pos_lable_beginIndex[0]));

    rangesToRemove.push_back(std::pair<size_t, size_t>(org_eegData.dataHead.pos_lable_endIndex[0] + 1, 
                                                        org_eegData.dataHead.pos_lable_beginIndex[1]));

    rangesToRemove.push_back(std::pair<size_t, size_t>(org_eegData.dataHead.pos_lable_endIndex[1] + 1, 
                                                        org_eegData.dataHead.pos_lable_beginIndex[2]));
    

    // // 删除最后一段没有用的数据
    // rangesToRemove.push_back(std::pair<size_t, size_t>(org_eegData.dataHead.pos_lable_endIndex[2] + 1, 
    //                                                     org_eegData.data.size()));
    for (const auto &range : rangesToRemove){
        std::cout << std::endl;
        std::cout << range.first << std::endl;
        std::cout << range.second << std::endl;
        size_t start = range.first;
        size_t end = range.second;
        // 保留的元素
        while (readIndex < start) {
            org_eegData.data[writeIndex++] = org_eegData.data[readIndex++];
        }
        // 跳过不要的元素
        readIndex = end;
    }
    
    size_t endIndex = org_eegData.dataHead.pos_lable_endIndex[2]+1;
    // 将最后一段 [readIndex, data.size()) 的元素移动到 writeIndex
    while (readIndex < endIndex) {
        org_eegData.data[writeIndex++] = org_eegData.data[readIndex++];
    }

    // 移除多余的数据
    org_eegData.data.resize(writeIndex);
    return org_eegData.data.size();
    #endif
}




// int BackgroundProgram::saveEEGData(const char* filePath, const GenEEGData& eegData, bool saveHead_flag){
//     std::cout << "This funtion has been abandoned..." << std::endl;
//     return -1;
// }

// GenEEGData BackgroundProgram::loadEEGData(const char* filePath){
//     std::cout << "This funtion has been abandoned..." << std::endl;
//     return GenEEGData();
// }


